/*************************************************************************************************************
*                                                    UTOS
*                               A non-preemptive(cooperative) multitasking RTOS Kernel
*
* Copyright (c) 2013, HuangLiang (huangliang.tj@gmail.com)
* Under New BSD license, see "LICENSE" for detail
*
* @.file       utos_process.c
* @.version    refer to macro "UT_VERSION" in utos.h
* @.brief      
* 
*************************************************************************************************************/
#include  "utos.h"


/*************************************************************************************************************
*                                         Global:  UT_Process_Init()
*
* @.brief     Initial PCB block
*
* @.argument  none.
*
* @.return    none.
*************************************************************************************************************/
void      UT_Process_Init(void)
{
    UT_INT32U  i;
    UT_PCB  *p1,*p2;
    
    UT_MemClr(&UT_PCB_Pool[0],sizeof(UT_PCB_Pool));
    UT_MemClr(&UT_PCB_Prio_Tbl[0], sizeof(UT_PCB_Prio_Tbl));
    
    p1 = &UT_PCB_Pool[0];
    p2 = &UT_PCB_Pool[1];
    
    p1->p_prev = (UT_PCB*)0;
#if  UT_PROCESS_NAME_EN > 0
    p1->p_pcb_name = (UT_INT8S*)0;
#endif

    for(i=0;i<UT_PROCESS_MAX-1;i++)
    {
        p1->p_next = p2;
        p2->p_prev = p1;
        p2->p_next = (UT_PCB*)0;
#if  UT_PROCESS_NAME_N > 0
        p2->p_pcb_name = (UT_INT8S*)0;
#endif
        p1++;
        p2++;
    }
    
    UT_PCB_List  = (UT_PCB*)0;
    // UT_PCB_List->p_next is always null
    UT_PCB_Free_Entry = &UT_PCB_Pool[0];
    // UT_PCB_Free_Entry->p_prev is always null.

    UT_PCB_Created_Num = 0;
    
    UT_PCB_Runtime_Cur = (UT_PCB*)0;                // Runtime pointer set to Null
}

/*************************************************************************************************************
*                                         Global:  UT_Process_Create()
*
* @.brief     Initial PCB block
*
* @.argument  (1). prio
*                     Priority of the process you want to create
*             (2). thread_pq_heap
*                     Thread heap for thread's priority queue (pq)
*             (2). thread_pq_size
*                     The max number of threads this process can contain
*
*
* @.return    .
*************************************************************************************************************/
UT_INT8U     UT_Process_Create(UT_INT8U prio, void ** thread_pq_heap, UT_INT32U thread_pq_size)
{
    UT_PCB  *p_pcb;
    if(prio>UT_PROCESS_LOWEST_PRIO)
        return UT_ERR_PROC_PRIO_INVALID;
    if(!UT_PCB_Free_Entry)
        return UT_ERR_PROC_FULL;
    if(!thread_pq_heap)
        return UT_ERR_PROC_HEAP_NULL;
    if(!(thread_pq_size>1))
        return UT_ERR_PROC_ZERO_THREAD;
    if(UT_PCB_Prio_Tbl[prio] != (UT_PCB*)0)
        return UT_ERR_PROC_PRIO_USED;
        
    p_pcb = UT_PCB_Free_Entry;
    UT_PCB_Free_Entry = UT_PCB_Free_Entry->p_next;
    if(UT_PCB_Free_Entry)
        UT_PCB_Free_Entry->p_prev = (UT_PCB*)0;
    
    p_pcb->p_prev           =   UT_PCB_List;
    p_pcb->p_next           =   (UT_PCB*)0;
    if(UT_PCB_List)
        UT_PCB_List->p_next     =   p_pcb;
    UT_PCB_List             =   p_pcb;
    
    p_pcb->tpq              =   thread_pq_heap;
    p_pcb->tpq_size         =   thread_pq_size;
    p_pcb->tpq_entry        =   1;                  // 0 index will never be used in pq
    p_pcb->p_tcb_list       =   (UT_TCB*)0;
    p_pcb->tcb_cnt          =   0;   
    
#if UT_PROCESS_LOWEST_PRIO <= 63
    p_pcb->rdy_grp          =   (UT_INT8U) ( prio / 8);
    p_pcb->rdy_tbl          =   (UT_INT8U) ( prio % 8);
    p_pcb->rdy_grp_bit      =   (UT_INT8U) ( 0x01 << p_pcb->rdy_grp );
    p_pcb->rdy_tbl_bit      =   (UT_INT8U) ( 0x01 << p_pcb->rdy_tbl );
#else
    p_pcb->rdy_grp          =   (UT_INT8U) ( prio / 16);
    p_pcb->rdy_tbl          =   (UT_INT8U) ( prio % 16);
    p_pcb->rdy_grp_bit      =   (UT_INT16U)( 0x01 << p_pcb->rdy_grp );
    p_pcb->rdy_tbl_bit      =   (UT_INT16U)( 0x01 << p_pcb->rdy_tbl );
#endif
    
    p_pcb->prio             =   prio;               // Here the prio is no doubt legal.
    p_pcb->stat             =   UT_PCB_STAT_READY_ANY;
    p_pcb->opt              =   UT_PCB_OPT_NONE;
    // Add Rdy Tbl set
    UT_PCB_Prio_Tbl[prio]   =   p_pcb;
    
    UT_PCB_Created_Num++;
    return  UT_ERR_NONE;
}
UT_INT8U     UT_Process_Create_Ext(UT_INT8U prio, void ** thread_pq_heap, UT_INT32U thread_pq_size,
                                    UT_INT8S *pcb_name, UT_INT8U pcb_opt)
{
    UT_INT8U err;
    UT_PCB  *p_pcb;
    if(!pcb_name)
        return UT_ERR_PROC_NAME_NONE;
    err = UT_Process_Create(prio, thread_pq_heap, thread_pq_size);
    if(err == UT_ERR_NONE)
    {
        p_pcb = UT_Process_Find_Proc_Prio(prio);
#if  UT_PROCESS_NAME_EN > 0
        p_pcb->p_pcb_name = pcb_name;
#endif
        p_pcb->opt = pcb_opt;
        return UT_ERR_NONE;
    }
    else
        return err;
}
/*************************************************************************************************************
*                                         Global:  UT_Process_Delete()
*
* @.brief     Delete a process
*
* @.argument  (1). prio
*                     Priority of the process you want to create
*
*
* @.return    .
*************************************************************************************************************/
UT_INT8U     UT_Process_Delete(UT_INT8U prio)
{
    UT_PCB  *p_pcb;

    p_pcb = UT_Process_Find_Proc_Prio (prio);
    if(!p_pcb)
        return UT_ERR_PROC_PRIO_NOT_EXIST;
    
    if(p_pcb == UT_PCB_List)                    // Move Created PCB header
    {
        UT_PCB_List = UT_PCB_List->p_prev;
        if(UT_PCB_List)
            UT_PCB_List->p_next = (UT_PCB*)0;
    }
    else
    {
        //if(p_pcb->p_next)
            p_pcb->p_next->p_prev  = p_pcb->p_prev;
        if(p_pcb->p_prev)
            p_pcb->p_prev->p_next  = p_pcb->p_next;
    }
    
    // Add to Free PCB list
    p_pcb->p_next       =   UT_PCB_Free_Entry;
    p_pcb->p_prev       =   (UT_PCB*)0;
    if(UT_PCB_Free_Entry)
        UT_PCB_Free_Entry->p_prev = p_pcb;
    UT_PCB_Free_Entry = p_pcb;

/* Not clearing other pcb properties, increase run-time speed
    p_pcb->tpq          =   (void**)0;
    p_pcb->tpq_size     =   0;
    p_pcb->tpq_entry    =   0;                  // 0 index will never be used in pq
    p_pcb->p_tcb_list   =   (UT_TCB*)0;
    p_pcb->tcb_cnt      =   0;   
    // 
    p_pcb->prio         =   0;
#if UT_PROCESS_LOWEST_PRIO <= 63
    p_pcb->rdy_grp          =   (UT_INT8U)0;
    p_pcb->rdy_tbl          =   (UT_INT8U)0;
    p_pcb->rdy_grp_bit      =   (UT_INT8U)0;
    p_pcb->rdy_tbl_bit      =   (UT_INT8U)0;
#else
    p_pcb->rdy_grp          =   (UT_INT8U)0;
    p_pcb->rdy_tbl          =   (UT_INT8U)0;
    p_pcb->rdy_grp_bit      =   (UT_INT16U)0;
    p_pcb->rdy_tbl_bit      =   (UT_INT16U)0;
#endif
    p_pcb->stat             =   0;              // Clear state
*/
    // Clear Rdy Tbl set
    UT_PCB_Prio_Tbl[prio]   = (UT_PCB*)0;
    
    UT_PCB_Created_Num--;
    return  UT_ERR_NONE;
}
/*************************************************************************************************************
*                                         Global:  UT_Process_Find_Proc_Prio()
*
* @.brief     Find a process by its prio
*
* @.argument  (1). proc_prio
*                     Priority of the process you want to find
*
*
* @.return    .
*************************************************************************************************************/
UT_PCB      * UT_Process_Find_Proc_Prio (UT_INT8U proc_prio)
{
    if(proc_prio == UT_PRIO_PROC_SELF)
    {
        return UT_PCB_Runtime_Cur;
    }

    if(proc_prio>UT_PROCESS_LOWEST_PRIO)
        return (UT_PCB*)0;
        
    return UT_PCB_Prio_Tbl[proc_prio];
            // Find process, if a process has not been created yet, it may return Null
}
UT_INT8U   UT_Process_Mute(UT_INT8U proc_prio)
{
    UT_PCB *p_proc;
    p_proc = UT_Process_Find_Proc_Prio (proc_prio);
    if(!p_proc)
        return UT_ERR_PROC_PRIO_NOT_EXIST;
    p_proc->stat |= UT_PCB_STAT_MUTE;
    return UT_ERR_NONE;
}
/*************************************************************************************************************
*                                                  END
*************************************************************************************************************/
